﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace OpenRefineEngine.GREL
{
    class GrelEngine
    {

        private List<string> Functions { get; set; }

        public GrelEngine(string grelExpression)
        {
            if (!grelExpression.StartsWith("grel:")) throw new Exception("GREL Engine couldn't understand: '" + grelExpression + "' it doesn't start with 'grel:'");
            grelExpression = grelExpression.Remove(0, "grel:".Length);
            Functions = grelExpression.Split('.').ToList();
        }

        /// <summary>
        /// Note: Expressions that involves multiple values (from other columns) or have inner functions or recursiveness are currently not supported
        /// </summary>
        /// <param name="value">value for "value" expression keyword</param>
        /// <returns>The result of performed expression</returns>
        public string ApplyExpression(string value)
        {
            //TODO: Currently dont perform recursive expressions:
            //like: grel:value.replace(replace(value, "foo", "bar"), "foobar") //will be read as literal "replace(value, "foo", "bar")"
            //like: grel:value.replace(value.replace("foo", "bar"), "foobar") //will not be performed because second argument in "replace(value" is not found.
            //because it will actually be /Recursive/ (Special case), but will be necessary when using multiple values
            var functions = Functions.ToArray();
            for (var i = 0; i < functions.Length; i++) if (functions[i] == "value") functions[i] = "\"" + value + "\"";
            var operations = functions.ToList();
            string lastValue = "";
            var quotes = new[] {'\'', '"'};
            foreach (var operation in operations)
            {
                if ((operation.StartsWith("\"") && operation.EndsWith("\""))
                    || operation.StartsWith("'") && operation.EndsWith("'")) //literals in GREL could be with both " and ' chars
                    lastValue = operation.Trim(quotes); //It's a literal
                else
                {
                    var operationName = operation.Substring(0, operation.IndexOf("(", StringComparison.Ordinal));
                    var operationArguments = operation.Substring(operation.IndexOf("(", StringComparison.Ordinal),
                                                                 operation.LastIndexOf("(", StringComparison.Ordinal)).Split(',');
                    switch (operationName)
                    {
                        case "strip":
                        case "trim":
                            lastValue = lastValue.Trim();
                            break;
                        case "replace":
                            lastValue = lastValue.Replace(operationArguments[0].Trim(quotes), operationArguments[1].Trim(quotes));
                            break;
                        case "toLowercase":
                            lastValue = lastValue.ToLowerInvariant();
                            break;
                        case "toUppercase":
                            lastValue = lastValue.ToUpperInvariant();
                            break;
                        case "toTitlecase":
                            lastValue = System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(lastValue.ToLower());
                            break;
                        case "md5":
                            lastValue = Hash.Md5(lastValue);
                            break;
                        //TODO: add all the rest of GREL needed functions
                    }
                }
            }
            return lastValue;
        }
    }
}
